package bluenext;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import lejos.nxt.remote.DeviceInfo;
import lejos.nxt.remote.FileInfo;
import lejos.nxt.remote.FirmwareInfo;
import lejos.nxt.remote.InputValues;

import lejos.nxt.remote.NXTCommand;
import lejos.nxt.remote.OutputState;
import lejos.pc.comm.NXTCommException;
import lejos.pc.comm.NXTInfo;

/**
 * Définit l'ensemble des fonctionnalités offertes par la librairie BlueNeXT.
 * C'est l'unique classe à instancier pour utiliser BlueNeXT. Une grande partie
 * des méthodes accessibles correspondent à un envoi d'une commande du protocole
 * LCP.
 *
 * @author Arnaud Hittos
 * @author Alan Macé
 * @author Aurélien Mariage
 * @version 1.0
 */
public class CommonInterface {

    /** Interface réseau de la librairie  */
    private bluenext.NetworkInterface ni;
    /** Liste des robots accessibles */
    private Vector<NXT> nxts;
    /** Permet l'envoi d'une commande LCP */
    private final NXTCommand command = NXTCommand.getSingleton();

    /**
     * Construteur de base.
     * Initialise les objets de l'interface.
     */
    public CommonInterface() {
        nxts = new Vector<NXT>();
        ni = new bluenext.NetworkInterface();
    }

    /**
     * Recherche les robots accessibles.
     * Les robots concernés sont allumés ou appairés avec le système
     * d'exploitation.
     * @return tableau de <code>String</code> composé des noms des robots trouvés
     * @throws ErreurNXTException
     * 		code ERREUR_CREATION_OBJET_NXT: erreur creation objet NXT
     * 		code ERREUR_SEARCH: La recherche des robot a echoue
     */
    public String[] search() throws ErreurNXTException {
        // demande de faire une recherche des NXTs allumés aux alentour
        NXTInfo[] nxtinf;
        try {
            nxtinf = ni.search();
        } catch (NXTCommException e) {
            throw new ErreurNXTException("La recherche des robots a echoue", ErreurNXTException.ERREUR_SEARCH);
        }

        //test des données reçues
        if ((nxtinf == null) || (nxtinf.length < 1)) {
            return null;
        } else {
            String[] nomNXTTrouve = new String[nxtinf.length];

            for (int i = 0; i < nxtinf.length; i++) {
                //test di le nxt est dans le tableau ou non
                if (getIdNXTName(nxtinf[i].name) == -1) {
                    //ajouter le nxt dans le tableau
                    NXT nxt = new NXT(nxtinf[i]);
                    addNXT(nxt);
                    System.out.println("NXT ajoute a nxts dans search()");
                }
                nomNXTTrouve[i] = nxtinf[i].name;
            }
            return nomNXTTrouve;
        }
    }

    /**
     * Etablit une connexion avec un robot
     * La fonction peut être longue à exécuter si le robot n'est pas référencé
     * dans la librairie, il faut alors faire appel à la méthode
     * <code>search()</code>.
     * @param nxtName nom du robot
     * @return statut de la connexion
     * @throws NXTCommException code ERREUR_REF_ROBOT : Le robot n'est pas connu
     * 				code ADRESS_INCORECT : l'adresse du du robot n'est pas correcte
     * 				code ERREUR_CONNEXION : la connexion a echoue
     * 				code ERREUR_CREATION_OBJET_NXT : erreur creation objet NXT
     */
    public boolean connect(String nxtName) throws ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            //faire un search pour mettre à jour la liste des robots
            search();
            nxt = getNXTByName(nxtName);
            if (nxt == null) {
                throw new ErreurNXTException("L'adresse du robot n'est pas connue", ErreurNXTException.ERREUR_REF_ROBOT);
            } else {
                return ni.connect(nxt);
            }
        } else {
            return ni.connect(nxt);
        }
    }

    /**
     * Etablit une connection avec un robot.
     * L'utilisation de l'adresse du robot évite de devoir faire une recherche.
     * @param nxtName nom du robot
     * @param nxtAddress adresse du robot
     * @return statut de la connexion
     * @throws NXTCommException code ADRESS_INCORECT : l'adresse du du robot n'est pas correcte
     * 				code ERREUR_CONNEXION : la connexion a echoue
     * 				code ERREUR_CREATION_OBJET_NXT : erreur creation objet NXT
     * */
    public boolean connect(String nxtName, String nxtAddress) throws ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            //cas où le robot n'est pas référencé
            NXTInfo inf = new NXTInfo();
            inf.name = nxtName;
            inf.deviceAddress = nxtAddress;
            nxt = new NXT(inf);
            //tenter une connection avec les informations fournies
            boolean res;
            res = ni.connect(nxt);

            if (res == true) {
                //ajout du nouveau robot dans le talbeau
                addNXT(nxt);
            }
            return res;
        } else {
            return ni.connect(nxt);

        }
    }

    /**
     * Ferme la connexion à un robot.
     * @param nxtName nom du robot
     * @throws IOException  */
    public void disconnect(String nxtName) throws IOException {
        ni.disconnect(getNXTByName(nxtName));
    }

    /**
     * Ferme la connexion à un robot.
     * @param nxtName nom du robot
     * @param nxtAddress adresse du robot
     * @throws IOException  */
    public void disconnect(String nxtName, String nxtAddress) throws IOException {
        ni.disconnect(getNXTByName(nxtName));
    }

    /**
     * Retourne l'adresse d'un robot.
     * @param nxtName nom du robot
     * @return adresse du robot
     */
    public String getAddressByName(String nxtName) {
        int id = getIdNXTName(nxtName);

        if (id == -1) {
            //cas où le robot n'es pas référancé
            return null;
        } else {
            String address = new String();
            address = nxts.get(id).getInfos().deviceAddress;
            return address;
        }
    }

    /**
     * Retourne le niveau de batrerie en millivolt d'un robot.
     * @param nxtName nom du robot
     * @return le niveau de baterie en millivolt
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public int getBatteryLevel(String nxtName) throws ErreurNXTException, IOException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.getBatteryLevel();
            }
        }
    }

    /**
     * Retourne les informations d'un robot.
     * @param nxtName nom du robot
     * @return objet <code>DeviceInfo</code> comportant les informations du robot NXT
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public DeviceInfo getDeviceInfo(String nxtName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.getDeviceInfo();
            }
        }
    }

    /**
     * Retourne les informations du firmware d'un robot.
     * @param nxtName nom du robot
     * @return objet <code>FirmwareInfo</code> donnant les informations du firmware
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     *                                  ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public FirmwareInfo getFirmwareVersion(String nxtName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.getFirmwareVersion();
            }
        }
    }

    /**
     * Envoie un message a un robot (commande LCP).
     * @param nxtName nom du robot
     * @param message message à envoyer au robot
     * @param inbox numero de la boite de reception (0-9)
     * @return 0 si succe
     * @throws IOException
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte messageWrite(String nxtName, byte[] message, byte inbox) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.messageWrite(message, inbox);
            }
        }
    }

    /**
     * Demande la lecture d'un message d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param remoteInbox vlaleur de 0 a 9
     * @param localInbox valeur de 0 a 9
     * @param remove indique si on suprime le message de la boite de reception ou non
     * @return message recue
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte[] messageRead(String nxtName, byte remoteInbox, byte localInbox, boolean remove) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.messageRead(remoteInbox, localInbox, remove);
            }
        }
    }

    /**
     * Envoi generique de données à un robot.
     * @param nxtName nom du robot
     * @param message message a envoyer au robot
     * @throws IOException
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public void write(String nxtName, byte[] message) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                // envoi du message
                OutputStream os = nxt.getNxtComm().getOutputStream();
                os.write(message);
                os.flush();
            }
        }
    }

    /**
     * Reçoit des données envoyees par un robot.
     * @param nxtName nom du robot
     * @param maxSize taille max du paquet à lire
     * @return donnees recues
     * @throws IOException
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte[] read(String nxtName, int maxSize) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                // reception message
                InputStream is = nxt.getNxtComm().getInputStream();
                byte[] buf = new byte[maxSize];
                if (is.read(buf) == -1) {
                    return null;
                } else {
                    return buf;
                }
            }
        }
    }

    /**
     * Ouvre un flux de lecture d'un fichier d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param fileName nom du fichier
     * @return structure FileInfo donnant les informations du fichier venant d'être ouvert
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public FileInfo openRead(String nxtName, String fileName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.openRead(fileName);
            }
        }
    }

    /**
     * Ouvre un flux d'ecriture sur un fichier d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param fileName nom du fichier
     * @param size
     * @return l'indentifiant du fichier
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte openWrite(String nxtName, String fileName, int size) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.openWrite(fileName, size);
            }
        }
    }

    /**
     * Retourne les informations du premier fichier correspondant au mot cle sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @param wildCard mot cle de recherche ([nomdufichier].[extansion])
     * @return objet <code>FileInfo</code> indicant les informations du fichier trouve
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public FileInfo findFirst(String nxtName, String wildCard) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.findFirst(wildCard);
            }
        }
    }

    /**
     * Retourne les informations du fichier suivant sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @param handle identifiant du fichier courant
     * @return objet <code>FileInfo</code> indicant les informations du fichirr trouve
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public FileInfo findNext(String nxtName, byte handle) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.findNext(handle);
            }
        }
    }

    /**
     * Supprime un fichier sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @param fileName nom du fichier
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 					ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte deleteFile(String nxtName, String fileName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.delete(fileName);
            }
        }
    }

    /**
     * Ferme un fichier sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @param handle identifiant du fichier
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte closeFile(String nxtName, byte handle) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.closeFile(handle);
            }
        }
    }

    /**
     * Lit un fichier d'un robot (commande LCP).
     * @param handle identifiant du fichier
     * @param length nombre d'octet a lire
     * @return donnees lues
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte[] readFile(String nxtName, byte handle, int length) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.readFile(handle, length);
            }
        }
    }

    /**
     * Ecrit des donnees dans un fichier d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param handle identifiant du fichier
     * @param data donnee a ecrire
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte writeFile(String nxtName, byte handle, byte[] data) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.writeFile(handle, data);
            }
        }
    }

    /**
     * Mettre a jour un fichier sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @param file nouveau fichier
     * @param nxtFileName nom du fichier a remplacer
     * @return message de compte rendu
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public String uploadFile(String nxtName, File file, String nxtFileName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.uploadFile(file, nxtFileName);
            }
        }
    }

    /**
     * Execute un fichier audio d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param fileName nom du fichier
     * @param repeat
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte playSoundFile(String nxtName, String fileName, boolean repeat) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.playSoundFile(fileName, repeat);
            }
        }
    }

    /**
     * Joue un son sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @param frequency frequence du son
     * @param duration duree du son
     * @return O si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte playTone(String nxtName, int frequency, int duration) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.playTone(frequency, duration);
            }
        }
    }

    /**
     * Stoppe le son joue sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte stopSoundPlayback(String nxtName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.stopSoundPlayback();
            }
        }
    }

    /**
     * Execute un programme d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param fileName nom du programme
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte startProgram(String nxtName, String fileName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.startProgram(fileName);
            }
        }
    }

    /**
     * Retourne le nom du programme qui est execute sur un robot (commande LCP).
     * @param nxtName nom du robot
     * @return nom du programme execute
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public String getCurrentProgramName(String nxtName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.getCurrentProgramName();
            }
        }
    }

    /**
     * Stoppe le programme en cours d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte stopProgram(String nxtName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.stopProgram();
            }
        }
    }

    /**
     * Supprime toutes les donnees de l'utilisateur (fichier, son, programmes...) (commande LCP).
     * @param nxtName nom du robot
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte deleteUserFlash(String nxtName) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.deleteUserFlash();
            }
        }
    }

    /**
     * Retourne les valeurs d'un port d'entree (1,2,3,4) d'un robot (commande LCP).
     * @param nxtName le nom du robot
     * @param port valeur du port
     * @return structure InputValue donnant les information du port d'entree
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException          */
    public InputValues getInputValues(String nxtName, int port) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.getInputValues(port);
            }
        }
    }

    /**
     * Retourne les valeurs de sortie d'un robot (A,B,C) (commande LCP).
     * @param nxtName nom du robot
     * @param port valeur de 0 a 2
     * @return structure OutputState donnant les informations du port de sortie
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public OutputState getOutputState(String nxtName, int port) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.getOutputState(port);
            }
        }
    }

    /**
     * Réinitialise la position d'un moteur d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param port valeur de 0 a 2
     * @param relative TRUE: BlockTacho, FALSE: RotationCount
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte resetMotorPosition(String nxtName, int port, boolean relative) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.resetMotorPosition(port, relative);
            }
        }
    }

    /**
     * Définit les informations du capteur branche sur un port d'entree d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param port valeurs de 0 a 3
     * @param sensorType
     * @param sensorMode
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte setInputMode(String nxtName, int port, int sensorType, int sensorMode) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.setInputMode(port, sensorType, sensorMode);
            }
        }
    }

    /**
     * Défini les informations du capteur branché sur un port de sortie d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param port (0 - 2 ou 0xFF pour les trois)
     * @param power (-100 à 100)
     * @param mode (modes MOTORON, BRAKE, et/ou REGULATED. Ce parametre est un champ de bit, pour le passer en mode 'brake' et 'regulated' => BRAKEMODE + REGULATED
     * @param regulationMode cf NXTProtocol
     * @param turnRatio (-100 à 100)
     * @param runState cf NXTProtocol
     * @param tachoLimit Nombre de degre a effectuer avant de s'arreter
     * @return le statut (0 en cas de succes)
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte setOutputState(String nxtName, int port, byte power, int mode, int regulationMode, int turnRatio, int runState, int tachoLimit) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.setOutputState(port, power, mode, regulationMode, turnRatio, runState, tachoLimit);
            }
        }
    }

    /**
     * Modifie le nom d'un robot (commande LCP).
     * @param nxtName anxien nom du robot
     * @param name nouveau nom du robot
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte setFriendlyName(String nxtName, String name) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                byte resultat = command.setFriendlyName(name);
                if (resultat != -1) {
                    //remplacement du nom dans la librairie
                    nxt.getInfos().name = new String(name);
                }
                return resultat;
            }
        }
    }

    /**
     * Retourne le statut du capteur d'ultrason d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param port valeur de 0 a 3
     * @return byte[0] = status, byte[1] = Bytes Ready (count of available bytes to read)
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte[] LSGetStatus(String nxtName, byte port) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.LSGetStatus(port);
            }
        }
    }

    /**
     * Retourne les valeurs du capteur d'ultrason d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param port valeur de 0 a 3
     * @return les donnees binaires du capteur
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte[] LSRead(String nxtName, byte port) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.LSRead(port);
            }
        }
    }

    /**
     * Envoie des informations au capteur d'ultrason d'un robot (commande LCP).
     * @param nxtName nom du robot
     * @param port valeur de 0 a 3
     * @param txData donnees a envoyer
     * @param rxDataLength taille des donnees
     * @return 0 si succes
     * @throws ErreurNXTException code 	ERREUR_REF_ROBOT : le robot n'est affecte a aucune adresse
     * 									ROBOT_PAS_CONNECTE : le robot n'est pas connecte
     * @throws IOException
     */
    public byte LSWrite(String nxtName, byte port, byte[] txData, byte rxDataLength) throws IOException, ErreurNXTException {
        NXT nxt = getNXTByName(nxtName);
        if (nxt == null) {
            throw new ErreurNXTException(nxtName + " n'est affecte a aucune adresse", ErreurNXTException.ERREUR_REF_ROBOT);
        } else {
            if (!nxt.isConnected()) {
                throw new ErreurNXTException("le robot " + nxtName + " n'est pas connecte", ErreurNXTException.ROBOT_PAS_CONNECTE);
            } else {
                command.setNXTComm(nxt.getNxtComm());
                return command.LSWrite(port, txData, rxDataLength);
            }
        }
    }

    
    /*-------------------fonctions privees-----------------------------*/

    /**
     * Retourne l'objet <code>NXT</code> à partir d'un nom de robot.
     * @param nxtName le nom du robot à chercher
     * @return <code>null</code> si le robot n'existe pas, l'objet <code>NXT</code>
     * correspondant sinon
     */
    private NXT getNXTByName(String nxtName) {

        int id = getIdNXTName(nxtName);
        if (id == -1) {
            //cas ou le robot n'est pas referance
            return null;
        } else {
            return nxts.get(id);
        }
    }

    /**
     * Retourne l'indice représentant la position d'un robot dans le tableau.
     * @param nxtName le nom du robot à chercher
     * @return un entier donnant la position dans le tableau
     */
    private int getIdNXTName(String nxtName) {
        //parcours du tableau
        int id = 0;
        //boucle temps qu'on est pas a la fin du tableau et que on a pas trouve l'element
        while ((id < nxts.size()) && (!(nxts.get(id).getInfos().name.equals(nxtName)))) {
            id++;
        }
        if (id >= nxts.size()) {
            //cas ou l'element n'est pas dans le tableau
            return -1;
        } else {
            //cas où on a trouvé l'élément
            return id;
        }

    }

    /**
     * Ajoute un robot à la structure de donnée.
     * @param nxt l'objet <code>NXT</code> à ajouter
     * @return <code>true</code>
     */
    private boolean addNXT(NXT nxt) {
        return nxts.add(nxt);
    }
}
